package cn.com.nes.common.wx;

import cn.hutool.cache.CacheUtil;
import cn.hutool.cache.impl.TimedCache;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSONObject;
import org.apache.commons.codec.binary.Base64;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.IOException;
import java.security.AlgorithmParameters;
import java.security.Security;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

/**
 * ToDo
 * Create by yishuai on 2022/6/7
 **/

public class WeChatUtils {

    /**
     * access token 缓存，两小时过期
     */
    private static final TimedCache<String, String> accessTokenCache = CacheUtil.newTimedCache(60 * 60 * 2);

    /**
     * 微信小程序 appid
     */
    private static final String APP_ID = "wx113fa165055ce979";
    /**
     * 微信小程序 appSecret
     */
    private static final String SECRET = "e014d7abd4d9dda249cd9802937d1c23";
    /**
     *  获取登陆凭证地址
     */
    private static final String AUTH_CODE_2_SESSION_URL= "https://api.weixin.qq.com/sns/jscode2session?appid=APPID&secret=SECRET&js_code=JSCODE&grant_type=authorization_code";
    /**
     * 获取AccessToken地址
     */
    private static final String GET_ACCESS_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET";
    /**
     * 获取手机号地址
     */
    private static final String GET_USER_PHONE_URL = "https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token=ACCESS_TOKEN";

    /**
     * 推送小程序订阅消息
     */
    private static final String SUBSCRIBE_MESSAGE_URL =
            "https://api.weixin.qq.com/cgi-bin/message/subscribe/send";

    /**
     * 获取open_id和session_key
     * @param code
     * @return
     * @throws IOException
     */
    public static String getOpenIdAndSessionKey(String code) throws IOException {
        String url = AUTH_CODE_2_SESSION_URL.replace("APPID", APP_ID).replace("SECRET", SECRET).replace("JSCODE", code);
        String result = HttpClientUtils.get(url);
        return result;
    }

    /**
     * 解密用户信息
     * @param encryptedData
     * @param sessionKey
     * @param iv
     * @return
     */
    public static JSONObject getUserInfo(String encryptedData, String sessionKey, String iv) {
        JSONObject jsonObject = null;
        // 被加密的数据
        byte[] dataByte = Base64.decodeBase64(encryptedData);
        // 加密秘钥
        byte[] keyByte = Base64.decodeBase64(sessionKey);
        // 偏移量
        byte[] ivByte = Base64.decodeBase64(iv);
        try{
            // 如果密钥不足16位，那么就补足.  这个if 中的内容很重要
            int base = 16;
            if (keyByte.length % base != 0) {
                int groups = keyByte.length / base + (keyByte.length % base != 0 ? 1 : 0);
                byte[] temp = new byte[groups * base];
                Arrays.fill(temp, (byte) 0);
                System.arraycopy(keyByte, 0, temp, 0, keyByte.length);
                keyByte = temp;
            }
            // 初始化
            Security.addProvider(new BouncyCastleProvider());
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding","BC");
            SecretKeySpec spec = new SecretKeySpec(keyByte, "AES");
            AlgorithmParameters parameters = AlgorithmParameters.getInstance("AES");
            parameters.init(new IvParameterSpec(ivByte));
            cipher.init(Cipher.DECRYPT_MODE, spec, parameters);// 初始化
            byte[] resultByte = cipher.doFinal(dataByte);
            if (null != resultByte && resultByte.length > 0) {
                String result = new String(resultByte, "UTF-8");
                return JSONObject.parseObject(result);
            }
        }catch (Exception e) {
            e.printStackTrace();
        }
        return jsonObject;
    }

    /**
     *
     * @param openId 用户的openId
     * @param templateId 订阅消息的模板id
     * @param page 小程序跳转页
     * @param data 订阅消息的数据
     */
    public static void subscribeMessage(String openId, String templateId, String page, JSONObject data) {
        try {
            String accessToken = getAccessToken(3);
            JSONObject params = new JSONObject();
            params.put("touser", openId);
            params.put("template_id", templateId);
            params.put("page", page);
            params.put("data", data);
            String result = HttpClientUtils.postJSON(SUBSCRIBE_MESSAGE_URL + "?access_token=" + accessToken, params);
            System.out.printf(result);
        } catch (Exception ignored) {
            System.out.println(ignored.getMessage());
        }
    }

    /**
     * 获取AccessToken
     * @return
     */
    public static JSONObject getAccessTokenFromWeChat() throws IOException {
        String url = GET_ACCESS_TOKEN_URL.replace("APPID", APP_ID).replace("APPSECRET", SECRET);
        String result = HttpClientUtils.get(url);
        return JSONObject.parseObject(result);
    }

    public static String getAccessToken() {
        //优先从缓存中获取
        String accessToken = accessTokenCache.get(APP_ID, false);
        if (StrUtil.isNotBlank(accessToken)) {
            return accessToken;
        }

        try {
            JSONObject json = getAccessTokenFromWeChat();
            //过期或者不存在，重新获取
            if (json.containsKey("access_token")) {
                accessToken = json.getString("access_token");
                accessTokenCache.put(APP_ID, accessToken, 60 * 60 * 2);
            }
        } catch (IOException ignored) {
        }

        return accessToken;
    }

    /**
     * 获取AccessToken（失败后，重新获取）
     * @param retryTimes 最大重试次数
     * @return
     */
    public static String getAccessToken(int retryTimes) {
        String accessToken = getAccessToken();
        if (StrUtil.isBlank(accessToken)) {
            for (int i = 0; i < retryTimes; i++) {
                accessToken = getAccessToken();
                if (StrUtil.isNotBlank(accessToken)) {
                    break;
                }
            }
        }
        return accessToken;
    }

    /**
     * 获取手机号
     * @param accessToken
     * @param code
     * @return
     */
    public static JSONObject getPhone(String accessToken, String code) throws IOException {
        String url = GET_USER_PHONE_URL.replace("access_token", accessToken);
        Map<String, String> param = new HashMap<>();
        String result = HttpClientUtils.post(url, param);
        return JSONObject.parseObject(result);
    }

    public static void main(String[] args) {
        System.out.println(WeChatUtils.getAccessToken());
    }

}
